package util.xmlmanager.elements;

import java.util.Iterator;
import java.util.LinkedList;

public class Element implements Content{
	// Constants:
	//private final String PROMPT = "Element> ";
	private final String TAB = "\t";
	private final String EMPTY_STRING = "";
	private final String LN = "\n";
	// Attributes:
	private String name;
	private LinkedList<Attribute> attributes;
	private LinkedList<Content> elements;
	private LinkedList<ProcessingInstruction> processingInstructions;

	// Constructors:
	public Element(String name, LinkedList<Attribute> attributes) {
		this.name = name;
		this.attributes = attributes;
		this.elements = new LinkedList<Content>();
		this.processingInstructions = new LinkedList<ProcessingInstruction>();
	}

	// Methods:
	public String getName() {
		return name;
	}

	public LinkedList<Element> getElements(){
		LinkedList<Element> ans = new LinkedList<Element>();
		for (Content content : this.elements) {
			if(content instanceof Element){
				ans.add((Element)content);
			}
		}
		return ans;
	}
	
	/**
	 * @param path
	 * @return selected elements in a linked list.
	 */
	public LinkedList<Element> find(LinkedList<String>path){
		return this.find(path,null);
	}
	@SuppressWarnings("unchecked")
	public LinkedList<Element> find(LinkedList<String>path, LinkedList<Attribute>constraints) {
		LinkedList<Element> ans = null;
		if(path!=null){
			if(path.size() == 1){
				String name = path.removeFirst();
				if(this.getName().equals(name)){
					if(this.constraints(constraints, this.attributes)){
						ans = new LinkedList<Element>();
						ans.add(this);
					}
				}
			}else if(path.size() > 1){
				String name = path.removeFirst();
				if(this.getName().equals(name)){
					ans = new LinkedList<Element>();
					for (Content content : this.elements) {
						if(content instanceof Element){
							LinkedList<Element> tmp = ((Element) content).find((LinkedList<String>) path.clone(),constraints);
							if(tmp != null){
								ans.addAll(tmp);
							}
						}
					}	
				}
			}
		}
		return ans;
	}
	private boolean constraints(LinkedList<Attribute>constraints, LinkedList<Attribute>localAttributes){
		if(constraints != null && !constraints.isEmpty() && localAttributes!=null && !localAttributes.isEmpty()){
			boolean isConstrained = true;
			for (Iterator<Attribute> iterator = constraints.iterator(); iterator.hasNext() && isConstrained;) {
				Attribute attribute = iterator.next();
				boolean thereIsSomeEqualAttribute = false;
				for (Iterator<Attribute> iterator2 = localAttributes.iterator(); iterator2.hasNext() && !thereIsSomeEqualAttribute;) {
					Attribute localAttribute = iterator2.next();
					if(attribute.equalsTo(localAttribute)){
						thereIsSomeEqualAttribute = true;
					}
				}
				isConstrained = isConstrained && thereIsSomeEqualAttribute;
			}
			return isConstrained;
		}else{
			return true;
		}
	}
	
	public void setData(String data) {
		this.elements.add(new Data(data));
	}

	public LinkedList<Attribute> getAttributes() {
		return attributes;
	}
	
	public LinkedList<Attribute> getAttributes(String attributeName) {
		LinkedList<Attribute> ans = new LinkedList<Attribute>();
		for (Attribute attribute: this.attributes) {
			if(attribute.getName().equals(attributeName)){
				ans.add(attribute); 
			}
		}
		return ans;
	}
	
	public Attribute getAttribute(String attributeName) {
		Attribute ans = null;
		for (Attribute attribute: this.attributes) {
			if(attribute.getName().equals(attributeName)){
				if(ans == null){
					ans = attribute; 
				}else{
					return null;
				}
			}
		}
		return ans;
	}

	public void addElement(Element element){
		this.elements.add(element);
	}
	
	public void addProcessingInstruction(ProcessingInstruction processingInstruction){
		this.processingInstructions.add(processingInstruction);
	}
	
	public String toString(){
		return this.getStringRepresentation(this.EMPTY_STRING);
	}
	private String getStringRepresentation(String tab){
		String currentTag = tab+"<"+this.name+((this.attributes!=null)?this.changeAttributesToString():this.EMPTY_STRING)+">",tab2;
		for (ProcessingInstruction processingInstruction : this.processingInstructions) {
			currentTag+=this.LN+tab+this.TAB+processingInstruction.toString();
		}
		if(!this.elements.isEmpty()){
			tab2 = tab;
			currentTag+=this.LN;
		}else{
			tab2 = this.EMPTY_STRING;
		}
		for (Content content: this.elements) {
			if(content instanceof Element){
				Element element = (Element)content;
				currentTag+=element.getStringRepresentation(tab+this.TAB);
			}else{
				Data data = (Data)content;
				currentTag+=tab+this.TAB+data.toString()+this.LN;
			}
		}
		currentTag+= tab2+"</"+this.name+">"+this.LN;
		return currentTag;
	}
	private String changeAttributesToString(){
		String args = "";
		if(this.attributes!=null){
			for (Attribute attribute : this.attributes) {
				args+=(" "+attribute.toString());
			}
		}
		return args;
	}
}
